import json
import sys
from datetime import datetime
from os import path
from pytz import timezone, all_timezones

from PyQt5.QtWidgets import (
    QApplication,
    QLabel,
    QWidget,
    QVBoxLayout,
    QSystemTrayIcon,
    QMenu,
    QAction,
    QGraphicsDropShadowEffect,
)
from PyQt5.QtGui import QIcon, QColor
from PyQt5.QtCore import Qt, QTimer


ALL_TIMEZONES = all_timezones


def ensure_icon_exists(icon_path="icon.svg"):
    if not path.exists(icon_path):
        default_svg_content = """<svg width="1024" height="1024" xmlns="http://www.w3.org/2000/svg"><rect stroke="null" height="1024" width="1024" fill="#fff" rx="200" ry="200" y="-2"/><g stroke="null" fill="currentColor"><path d="M516 823.714a307.714 307.714 0 1 0 0-615.428 307.714 307.714 0 0 0 0 615.428M516 875a359 359 0 1 1 0-718 359 359 0 0 1 0 718"/><path d="M490.357 310.857A25.643 25.643 0 0 1 516 336.5v205.143a25.643 25.643 0 0 1-51.286 0V336.5a25.643 25.643 0 0 1 25.643-25.643"/><path d="M490.357 516H695.5q25.643 0 25.643 25.643T695.5 567.286H490.357q-25.643 0-25.643-25.643T490.357 516"/></g></svg>"""
        with open(icon_path, "w") as f:
            f.write(default_svg_content)


class ConfigManager:
    DEFAULT_SETTINGS = {
        "timezone": "UTC",
        "style_timezone": "Asia/Shanghai",
        "position": [200, 100],
        "background_color_day": "rgba(255, 255, 255, 200)",
        "background_color_night": "rgba(25, 25, 25, 200)",
        "font_color_day": "rgba(0, 0, 0, 255)",
        "font_color_night": "rgba(255, 255, 255, 225)",
        "shadow_color_day": "rgba(125, 125, 125, 180)",
        "shadow_color_night": "rgba(25, 25, 25, 180)",
        "good_morning_time": "06:00",
        "good_night_time": "22:00",
        "keep_on_top": True,
    }

    def __init__(self, settings_file="settings.json"):
        self.settings_file = settings_file
        self.load_settings()

    def load_settings(self):
        try:
            with open(self.settings_file, "r") as file:
                self.settings = json.load(file)
        except (FileNotFoundError, json.JSONDecodeError):
            self.settings = self.DEFAULT_SETTINGS
            self.save_settings()

    def save_settings(self):
        with open(self.settings_file, "w") as file:
            json.dump(self.settings, file, indent=4)

    def get_setting(self, key):
        return self.settings.get(key, self.DEFAULT_SETTINGS[key])

    def set_setting(self, key, value):
        self.settings[key] = value
        self.save_settings()


class DesktopClock(QWidget):
    TIME_FORMAT = "%H:%M:%S"
    DEFAULT_POSITION = [100, 100]

    def __init__(self, app: QApplication):
        super().__init__()

        self.app = app
        self.config_manager = ConfigManager()
        self.initUI()
        self.create_tray_icon()
        self.create_tray_menu()
        self.create_timer()
        self.update_time()
        self.drag_position = None

    def create_tray_icon(self):
        self.tray_icon = QSystemTrayIcon(self)
        self.tray_icon.setIcon(QIcon("icon.svg"))
        self.tray_icon.setVisible(True)

    def toggle_keep_on_top(self, checked):
        self.config_manager.set_setting("keep_on_top", checked)
        if checked:
            self.setWindowFlags(self.windowFlags() | Qt.WindowStaysOnTopHint)
        else:
            self.setWindowFlags(self.windowFlags() & ~Qt.WindowStaysOnTopHint)
        self.show()

    def check_timezone(self, checked, timezone_name):
        checked_prefix = None
        for prefix, menu in self.prefix_menus.items():
            for action in menu.actions():
                action.setChecked(False)
                if action.text() == timezone_name:
                    action.setChecked(True)
                    checked_prefix = prefix
            menu_title = menu.title() if menu.title()[0] != "*" else menu.title()[1:]
            menu.setTitle(prefix)
            if checked_prefix != None and checked_prefix == menu_title:
                menu.setTitle("*" + prefix)

        self.config_manager.set_setting("timezone", timezone_name)
        self.update_time()

    def close(self):
        super().close()
        self.app.exit()

    def create_timezone_menu(self):
        # 设置时区
        self.timezone_menu = QMenu("Timezone", self)
        self.timezone_menu.setTitle("Timezone")
        checked_timezone = self.config_manager.get_setting("timezone")

        # 自定义前缀列表
        prefixes = [
            "Africa",
            "America",
            "Asia",
            "Australia",
            "Etc",
            "Europe",
            "Pacific",
            "Other",
        ]
        # 创建一个字典来存储每个前缀对应的子菜单
        self.prefix_menus = {
            prefix: QMenu(prefix, self.timezone_menu) for prefix in prefixes
        }

        # 遍历所有时区，根据前缀分配到相应的子菜单或默认分组
        for tz in ALL_TIMEZONES:
            added = False
            for prefix, menu in self.prefix_menus.items():
                if tz.startswith(prefix):
                    action = QAction(tz, self, checkable=True)
                    action.triggered.connect(
                        lambda checked, tz=tz: self.check_timezone(checked, tz)
                    )
                    menu.addAction(action)
                    added = True
                    break
            if not added:
                action = QAction(tz, self, checkable=True)
                action.triggered.connect(
                    lambda checked, tz=tz: self.check_timezone(checked, tz)
                )
                if tz == checked_timezone:
                    action.setChecked(True)
                self.prefix_menus["Other"].addAction(action)

        # 将非空的子菜单添加到主菜单
        for menu in self.prefix_menus.values():
            if menu.actions():
                self.timezone_menu.addMenu(menu)

        # 初始化选项
        self.check_timezone(True, checked_timezone)

        return self.timezone_menu

    def move_to_center(self):
        screen = QApplication.primaryScreen()
        center = screen.availableGeometry().center()
        self.move(center - self.rect().center())
        self.config_manager.set_setting("position", [self.x(), self.y()])

    def create_move_to_center_menu(self):
        move_to_center_action = QAction("Move to center", self)
        move_to_center_action.triggered.connect(self.move_to_center)
        return move_to_center_action

    def create_keep_on_top_menu(self):
        is_keep_on_top = self.config_manager.get_setting("keep_on_top")
        self.toggle_keep_on_top(is_keep_on_top)
        keep_top_action = QAction("Keep on top", self, checkable=True)
        keep_top_action.setChecked(is_keep_on_top)
        keep_top_action.triggered.connect(
            lambda checked: self.toggle_keep_on_top(checked)
        )
        return keep_top_action

    def create_exit_menu(self):
        exit_action = QAction("Exit", self)
        exit_action.triggered.connect(self.close)
        return exit_action

    def create_tray_menu(self):
        tray_menu = QMenu()
        tray_menu.addMenu(self.create_timezone_menu())
        tray_menu.addAction(self.create_move_to_center_menu())
        tray_menu.addAction(self.create_keep_on_top_menu())
        tray_menu.addAction(self.create_exit_menu())
        self.tray_icon.setContextMenu(tray_menu)

    def create_timer(self):
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.update_time)
        self.timer.start(1000)

    def is_night(self):
        tz = self.config_manager.get_setting("style_timezone")
        now = datetime.now(timezone(tz))
        day_start = self._create_time_from_setting("good_morning_time")
        night_start = self._create_time_from_setting("good_night_time")
        return now.time() < day_start.time() or now.time() >= night_start.time()

    def _create_time_from_setting(self, setting_key):
        time_str = self.config_manager.get_setting(setting_key)
        return datetime.fromisoformat(f"{datetime.now().date().isoformat()}T{time_str}")

    @property
    def background_color(self):
        return self.config_manager.get_setting(
            "background_color_night" if self.is_night() else "background_color_day"
        )

    @property
    def shadow_color(self):
        return self.parsingRgba(
            self.config_manager.get_setting(
                "shadow_color_night" if self.is_night() else "shadow_color_day"
            )
        )

    @property
    def font_color(self):
        return self.config_manager.get_setting(
            "font_color_night" if self.is_night() else "font_color_day"
        )

    def parsingRgba(self, rgba):
        rgb = rgba.replace("rgba", "").replace("(", "").replace(")", "").split(",")
        return QColor(int(rgb[0]), int(rgb[1]), int(rgb[2]), int(rgb[3]))

    def update_style(self):
        self.time_label.setStyleSheet(
            f"""
            font-size: 28px;
            font-weight: 1000;
            padding: 5px;
            background-color: {self.background_color};
            color: {self.font_color};
            border-radius: 10px;
        """
        )
        shadow_effect = QGraphicsDropShadowEffect(self)
        shadow_effect.setBlurRadius(15)
        shadow_effect.setColor(self.shadow_color)
        shadow_effect.setOffset(3, 3)
        self.time_label.setGraphicsEffect(shadow_effect)

    def initUI(self):
        ensure_icon_exists()
        self.setWindowTitle("Win11 Clock")
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.Tool)
        self.setAttribute(Qt.WA_TranslucentBackground)

        layout = QVBoxLayout()
        self.time_label = QLabel(self)
        self.time_label.setAlignment(Qt.AlignCenter)
        self.update_style()
        layout.addWidget(self.time_label)
        self.setLayout(layout)

        position = self.config_manager.get_setting("position")
        self.setGeometry(position[0], position[1], 200, 100)

    def update_time(self):
        tz = self.config_manager.get_setting("timezone")
        now = datetime.now(timezone(tz))
        current_time = now.strftime(self.TIME_FORMAT)
        self.time_label.setText(current_time)
        self.update_style()

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.drag_position = event.globalPos() - self.frameGeometry().topLeft()
            event.accept()

    def mouseMoveEvent(self, event):
        if event.buttons() == Qt.LeftButton and self.drag_position is not None:
            self.move(event.globalPos() - self.drag_position)
            event.accept()

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.drag_position = None
            QTimer.singleShot(1000, self.save_new_position)
            event.accept()

    def save_new_position(self):
        new_position = [self.x(), self.y()]
        self.config_manager.set_setting("position", new_position)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    clock = DesktopClock(app)
    clock.show()
    sys.exit(app.exec_())
